# +----------------------------------------------------------------------
# | ChatWork智能聊天办公系统
# +----------------------------------------------------------------------
# | 软件声明: 本系统并非自由软件,未经授权任何形式的商业使用均属非法。
# | 版权保护: 任何企业和个人不允许对程序代码以任何形式任何目的复制/分发。
# | 授权要求: 如有商业使用需求,请务必先与版权所有者取得联系并获得正式授权。
# +----------------------------------------------------------------------
# | Author: ChatWork Team <2474369941@qq.com>
# +----------------------------------------------------------------------
import json
import os
import requests
from exception import AppException
from common.utils.urls import UrlUtil
from common.utils.tools import ToolsUtil
from common.utils.config import ConfigUtil
from common.enums.pool import PoolEnum
from common.enums.voice import VoiceEnum
from common.cache.pool_cache import KeyPoolCache
from common.chain.voices_server import OpenaiAudio
from common.chain.voices_server import XunfeiAudio
from common.models.agents import AgentRobotModel
from common.models.agents import AgentChatRecordsModel
from apps.api.schemas import voice_schema as schema


class VoiceService:

    @classmethod
    async def speech(cls, scene: str, rid: int) -> schema.VoiceSpeechVo:
        """
        文本转语音

        Args:
            scene (str): 场景: [bot]
            rid (int): 来源ID

        Returns:
            schema.VoiceSpeechVo

        Author:
            zero
        """
        # 验证开关
        is_open: int = int(await ConfigUtil.get("voice_speech", "is_open", "0"))
        if is_open == 0:
            raise AppException("语音播放功能尚未开启")

        # 获取对话
        record = await cls._query_chat_record(scene, rid)

        # 读取配置
        setting: dict = VoiceEnum.get_default_speech()
        caches = await ConfigUtil.get("voice_speech", "configs") or {}
        if scene == "bot":
            robot = await AgentRobotModel.filter(id=record["robot_id"]).first().values("broadcast")
            broad = json.loads(robot.get("broadcast") or "{}")
            model = broad.get("model", "").split(":")
            channel: str = model[0]
            default: dict = setting.get(channel) or {}
            configs: dict = {**default, **caches[channel], **{
                "open": 1 if channel else 0,
                "voice": model[1] if len(model) > 2 else "",
                "speed": broad.get("speed", default.get("speed", 0))
            }}
        else:
            channel: str = await ConfigUtil.get("voice_speech", "channel") or ""
            default: dict = setting.get(channel) or {}
            configs: dict = {**default, **caches[channel]}

        # 读取密钥
        poolCache = KeyPoolCache(scene=PoolEnum.TYPE_VOICE_SPEECH, channel=channel)
        keys: dict = await poolCache.get_key()
        print(keys)
        if not keys:
            raise AppException("语音播放密钥尚未配置")
        if not configs.get("open"):
            raise AppException("语音播放通道尚未开启")

        # 音频路径
        md5: str = f'{scene}{channel}{record["id"]}{configs["voice"]}{configs["speed"]}{configs["volume"]}'
        filename: str = ToolsUtil.make_md5_str(md5) + ".mp3"
        save_dir: str = "storage/dialogue/voice/"
        file_url: str = save_dir + filename
        file_path: str = "public/" + save_dir + filename
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)

        # 音频缓存
        engine: str = await UrlUtil.get_storage_engine()
        if engine == "local":
            if os.path.exists(file_path):
                audio_url: str = await UrlUtil.to_absolute_url(file_url)
                return schema.VoiceSpeechVo(url=audio_url)
        else:
            try:
                audio_url: str = await UrlUtil.to_absolute_url(file_url)
                response = requests.head(audio_url, allow_redirects=True, timeout=5)
                if response.status_code == 200:
                    return schema.VoiceSpeechVo(url=audio_url)
            except requests.RequestException:
                pass

        # 发起转换
        try:
            if channel == "xunfei":
                await XunfeiAudio.speech(
                    input_text=record["answer"],
                    file_path=file_path,
                    app_id=keys.get("app_id"),
                    api_key=keys.get("api_key"),
                    api_secret=keys.get("api_secret")
                )
            elif channel == "openai":
                await OpenaiAudio.speech(
                    input_text=record["answer"],
                    file_path=file_path,
                    api_key=keys.get("api_key"),
                    base_url=configs.get("agency_api")
                )
        except Exception as e:
            if isinstance(e.args, tuple) and len(e.args) >= 2:
                raise AppException(str(e.args[1]))
            else:
                raise AppException(str(e))

        # 响应结果
        audio_url: str = await UrlUtil.to_absolute_url(file_url)
        return schema.VoiceSpeechVo(url=audio_url)

    @classmethod
    async def transcriptions(cls):
        # await OpenaiAudioServer.transcriptions()
        await OpenaiAudio.transcriptions(
            file_path="",
            api_key="",
            base_url=""
        )

    @classmethod
    async def _query_chat_record(cls, scene: str, rid: int):
        record = None
        if scene == "idle":
            pass
        else:
            fields = ["id", "robot_id", "answer"]
            record = await AgentChatRecordsModel.filter(id=rid, is_delete=0).first().values(*fields)
            if not record:
                raise AppException("对话记录已不存在")
            if not record["answer"]:
                raise AppException("播放内容不能为空")

        return record
